using System;
using System.Threading;

namespace LiteNetLib
{
	internal sealed class NetPacketPool
	{
		private readonly NetPacket[] _pool = new NetPacket[1000];

		private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

		private int _count;

		public NetPacket GetWithData(PacketProperty property, byte[] data, int start, int length)
		{
			NetPacket withProperty = GetWithProperty(property, length);
			Buffer.BlockCopy(data, start, withProperty.RawData, NetPacket.GetHeaderSize(property), length);
			return withProperty;
		}

		public NetPacket GetPacket(int size, bool clear)
		{
			NetPacket netPacket = null;
			if (size <= NetConstants.MaxPacketSize)
			{
				_lock.EnterUpgradeableReadLock();
				if (_count > 0)
				{
					_lock.EnterWriteLock();
					_count--;
					netPacket = _pool[_count];
					_pool[_count] = null;
					_lock.ExitWriteLock();
				}
				_lock.ExitUpgradeableReadLock();
			}
			if (netPacket == null)
			{
				netPacket = new NetPacket(size);
			}
			else
			{
				netPacket.Realloc(size, clear);
			}
			return netPacket;
		}

		public NetPacket GetWithProperty(PacketProperty property, int size)
		{
			size += NetPacket.GetHeaderSize(property);
			NetPacket packet = GetPacket(size, true);
			packet.Property = property;
			return packet;
		}

		public void Recycle(NetPacket packet)
		{
			if (packet.RawData.Length <= NetConstants.MaxPacketSize)
			{
				packet.RawData[0] = 0;
				_lock.EnterUpgradeableReadLock();
				if (_count == 1000)
				{
					_lock.ExitUpgradeableReadLock();
					return;
				}
				_lock.EnterWriteLock();
				_pool[_count] = packet;
				_count++;
				_lock.ExitWriteLock();
				_lock.ExitUpgradeableReadLock();
			}
		}
	}
}
